# Flask based web API server
from flask import Flask, request, jsonify, make_response
from flask_jwt_extended import (
    JWTManager, jwt_required, create_access_token,
    get_jwt_identity, get_jwt, decode_token, set_access_cookies,
    unset_jwt_cookies
)
from datetime import datetime, timedelta
from functools import wraps
import jwt
import bcrypt
from flask_cors import CORS
from mgmt.qstn_mgmt import QstnMgmt

app = Flask(__name__)
CORS(app)  # 允许跨域请求
# # CORS(app, supports_credentials=True)
# # **关键配置：**
# # 1. 使用 CORS 函数
# # 2. resources 参数指定要应用 CORS 的路由（'*' 表示所有路由）
# # 3. origins 参数指定允许的来源。可以是具体的源字符串，也可以是列表。
# #    这里允许来自 8848 端口的请求。
# # 4. supports_credentials=True 允许携带凭证（如 cookies），如果你的请求需要则必须设置。
# # --- 自定义认证装饰器 ---
# def require_auth(f):
#     def wrapper(*args, **kwargs):
#         # 检查是否是 OPTIONS 请求，如果是，直接放行让 CORS 处理
#         if request.method == 'OPTIONS':
#             return f(*args, **kwargs) # 让原始视图函数处理，或者让 flask-cors 处理

#         auth_header = request.headers.get('Authorization')
#         if not auth_header or 'YourExpectedToken' not in auth_header:
#             # 认证失败，返回 401
#             abort(401, description="Unauthorized: Missing or invalid token")
#         # 认证通过，继续执行视图函数
#         return f(*args, **kwargs)
#     # 确保包装后的函数有正确的 __name__ 和 __module__，这对某些 Flask 扩展很重要
#     wrapper.__name__ = f.__name__
#     wrapper.__module__ = f.__module__
#     return wrapper
# # --- End 自定义认证装饰器 ---
# CORS(app, resources={r"/*": {"origins": "http://192.168.1.103:8848"}}, supports_credentials=True)

# 配置
app.config['JWT_SECRET_KEY'] = 'your-secret-key'  # 生产环境应使用环境变量
app.config['JWT_ACCESS_TOKEN_EXPIRES'] = timedelta(hours=2)  # 访问令牌有效期
app.config['JWT_REFRESH_TOKEN_EXPIRES'] = timedelta(days=7)  # 刷新令牌有效期

jwtManager = JWTManager(app)

# 用户存储（生产环境应使用数据库）
users = {
    "admin": {
        "username": "admin",
        "password_hash": bcrypt.hashpw(b"admin123", bcrypt.gensalt()).decode()
    }
}

# 登录接口
@app.route('/login', methods=['POST'])
def login():
    data = request.get_json()
    
    # 验证用户
    user = users.get(data['username'])
    # if not user or not bcrypt.checkpw(data['password'].encode(), user['password_hash'].encode()):
    #     return jsonify({"error": "Invalid credentials"}), 401
    
    # 生成令牌
    access_token = create_access_token(
        identity=user['username'],
        expires_delta=app.config['JWT_ACCESS_TOKEN_EXPIRES']
    )
    print(f'access_token: {access_token};')
    refresh_token = create_access_token(
        identity=user['username'],
        expires_delta=app.config['JWT_REFRESH_TOKEN_EXPIRES']
    )
    print(f'refresh_token: {refresh_token};')
    # 返回令牌（实际应用中建议使用HTTP-only cookie）
    response = jsonify({
        "success": True,
        "message": "Login successful",
        "data": {
            "avatar": "https://avatars.githubusercontent.com/u/44761321",
            "username": "admin",
            "nickname": "小铭",
            "roles": ["admin"],
            "permissions": ["*:*:*"],
            # 按钮级别权限
            # 一个用户可能有多个角色
            "accessToken": access_token,
            "refreshToken": refresh_token,
            "expires": "2030/10/30 00:00:00"
        }
    })
    return response

# 界面左侧菜单
@app.route('/get-async-routes', methods=['GET'])
def get_async_routes():
    pqb_menu = {
        "path": "/pqb",
        "redirect": "/pqb/exercise",
        "meta": {
            "icon": "ri/information-line",
            # showLink: false,
            "title": "个人题库",
            "rank": 90
        },
        "children": [
            {
            "path": "/pqb/exercise",
            "name": "pqb_exercise",
            #"component": () => import("@/views/pqb/exercise.vue"),
            "meta": {
                "title": "练习场"
            }
            },
            {
            "path": "/pqb/review",
            "name": "pqb_review",
            #"component": () => import("@/views/pqb/review.vue"),
            "meta": {
                "title": "复习"
            }
            },
            {
            "path": "/pqb/statistics",
            "name": "pqb_statistics",
            #"component": () => import("@/views/pqb/statistics.vue"),
            "meta": {
                "title": "统计"
            }
            }
        ]
    }
    qbmg_menu = {
        "path": "/qbmg",
        "redirect": "/qstn/qstn_mgmt",
        "meta": {
            "icon": "ri/information-line",
            # showLink: false,
            "title": "题库管理",
            "rank": 10
        },
        "children": [
            {
                "path": "/qstn/qstn_mgmt",
                "name": "qstn_qstn_mgmt",
                #"component": () => import("@/views/pqb/exercise.vue"),
                "meta": {
                    "title": "题目管理"
                }
            },
            {
                "path": "/qstn/kp_mgmt",
                "name": "qstn_kp_mgmt",
                #"component": () => import("@/views/pqb/review.vue"),
                "meta": {
                    "title": "知识点管理"
                }
            },
            {
                "path": "/qstn/qstn_add",
                "name": "qstn_qstn_add",
                #"component": () => import("@/views/pqb/review.vue"),
                "meta": {
                    "title": "题目添加",
                    "showLink": False
                }
            },
            {
                "path": "/qstn/qstn_kp_mgmt",
                "name": "qstn_kp_qstn_kp_mgmt",
                #"component": () => import("@/views/pqb/statistics.vue"),
                "meta": {
                    "title": "题目知识点管理"
                }
            }
        ]
    }
    menus = [pqb_menu, qbmg_menu]
    return jsonify({"success": True, "data": menus})


# 界面左侧菜单
@app.route('/getPqbExercise', methods=['GET'])
def getPqbExercise():
    # 单选题
    # qna = [
    #     "# v0.0.1 例1.1\n设$f(x+\\frac{1}{x})=\\frac{x+x^{3}}{1+x^{4}}$，则$f(x)=$_____。",
    #     '<input type="radio" id="radioButton1" name="radioButtons" value="301" /> A. $f(x)=e^{x}$',
    #     '<input type="radio" id="radioButton2" name="radioButtons" value="302" /> B. $f(x)=\\ln(x+\\sqrt{1+x^{2}})$',
    #     '<input type="radio" id="radioButton3" name="radioButtons" value="303" /> C. $f(x)=\\frac{e^{x}-e^{-x}}{2}$',
    #     '<input type="radio" id="radioButton4" name="radioButtons" value="304" /> D. $f(x)=x$'
    # ]
    # # 多选题
    # qna = [
    #     '# v0.0.2 例1.1\n设$f(x+\\frac{1}{x})=\\frac{x+x^{3}}{1+x^{4}}$，则$f(x)=$_____。',
    #     '<input type="checkbox" id="opt_501" name="checkBoxes" value="501"><label for="501"> A. $f(x)=e^{x}$</label>',
    #     '<input type="checkbox" id="opt_502" name="checkBoxes" value="502"><label for="502"> B. $f(x)=\\ln(x+\\sqrt{1+x^{2}})$</label>',
    #     '<input type="checkbox" id="opt_503" name="checkBoxes" value="503"><label for="503"> C. $f(x)=\\frac{e^{x}-e^{-x}}{2}$</label>',
    #     '<input type="checkbox" id="opt_504" name="checkBoxes" value="504"><label for="504"> D. $f(x)=x$</label>'
    # ]
    # qna = ['v0.0.2 例1.1\n设$f(x+\\frac{1}{x})=\\frac{x+x^{3}}{1+x^{4}}$，则$f(x)=$<input type="text" id="itx_601" name="inputs" />，其一阶导数为<input type="text" id="itx_602" name="inputs" />，其二阶导数为：<input type="text" id="itx_603" name="inputs" />']
    qstn_id = 10
    qna = QstnMgmt.get_qstn_by_id(qstn_id)
    options = QstnMgmt.get_qstn_options(qstn_id)
    return jsonify({
        "success": True,
        "data": {
            'qna': qna,
            'options': options
        }
    })


# 保存问题
@app.route('/addQstn', methods=['POST'])
def addQstn():
    data = request.get_json()
    print(f'data: {type(data)}; \n{data};')
    print(f'添加题目接口 v0.0.1')
    if data['type'] == 'single':
        QstnMgmt.add_single(data)
    
    # params = {}
    # params['qstn_title'] = 'q1'
    # params['notes'] = 'r1'
    # QstnDao.add_qstn(params)
        
    resp = {'success': True, 'message': 'add question is ok', 
        'data': {
            'code': 0,
            'qstn_id': 102
        }
    }
    return jsonify(resp)








#############################################################################################################################
#############################################################################################################################
#############################################################################################################################
#############################################################################################################################
#############################################################################################################################

# 受保护接口
@app.route('/protected', methods=['GET'])
@jwt_required()
def protected():
    current_user = get_jwt_identity()
    return jsonify(logged_in_as=current_user)

# 刷新令牌接口
@app.route('/refresh', methods=['POST'])
@jwt_required(refresh=True)
def refresh():
    current_user = get_jwt_identity()
    new_access_token = create_access_token(
        identity=current_user,
        expires_delta=app.config['JWT_ACCESS_TOKEN_EXPIRES']
    )
    return jsonify(access_token=new_access_token)

# 令牌验证装饰器
def token_required(f):
    @wraps(f)
    def decorated(*args, **kwargs):
        token = request.headers.get('Authorization')
        if not token:
            print(f'token_error 1')
            return jsonify({"error": "Missing token"}), 401
        
        try:
            # 解码令牌
            decoded = jwt.decode(
                token.replace('Bearer ', ''),
                app.config['JWT_SECRET_KEY'],
                algorithms=['HS256']
            )
            
            # 验证令牌过期
            if decoded['exp'] < datetime.utcnow().timestamp():
                print(f'token_error 2')
                return jsonify({"error": "Token expired"}), 401
                
            # 验证用户存在
            if decoded['sub'] not in users:
                print(f'token_error 3')
                return jsonify({"error": "Invalid user"}), 401
                
        except jwt.ExpiredSignatureError:
            print(f'token_error 4')
            return jsonify({"error": "Token expired"}), 401
        except jwt.InvalidTokenError:
            print(f'token_error 5')
            return jsonify({"error": "Invalid token"}), 401
            
        return f(*args, **kwargs)
    return decorated

# 使用自定义装饰器的受保护接口
@app.route('/protected-custom', methods=['GET'])
@token_required
def protected_custom():
    current_user = get_jwt_identity()
    return jsonify(logged_in_as=current_user)

# 令牌验证中间件（全局验证）
@app.before_request
def before_request():
    if request.path in ['/login', '/refresh', '/get-async-routes']:
        return
    token = request.headers.get('Authorization')
    if not token:
        print(f'token_error 6 headers: {request.headers};')
        return jsonify({"error": "Missing token"}), 401
        
    try:
        decoded = jwt.decode(
            token.replace('Bearer ', ''),
            app.config['JWT_SECRET_KEY'],
            algorithms=['HS256']
        )
        if decoded['exp'] < datetime.utcnow().timestamp():
            print(f'token_error 7')
            return jsonify({"error": "Token expired"}), 401
        if decoded['sub'] not in users:
            print(f'token_error 8')
            return jsonify({"error": "Invalid user"}), 401
    except jwt.PyJWTError:
        print(f'token_error 9')
        return jsonify({"error": "Invalid token"}), 401

# 令牌注销接口
@app.route('/logout', methods=['POST'])
def logout():
    response = jsonify({"message": "Logout successful"})
    unset_jwt_cookies(response)
    return response

if __name__ == '__main__':
    app.run(host='0.0.0.0', port=5000, debug=False)